package per.hnvcam.searchext.services.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import per.hnvcam.net.http.HTTPConnection;
import per.hnvcam.searchext.Constants;
import per.hnvcam.searchext.data.dao.ConfigDAO;
import per.hnvcam.searchext.data.model.*;
import per.hnvcam.searchext.services.ClassificationService;
import per.hnvcam.searchext.services.SpecialQuery;
import per.hnvcam.searchext.services.TrainingService;

@Service("SpecialQuery")
public class SpecialQueryImpl extends CallbackSupportServiceImpl implements SpecialQuery {

   @Autowired
   @Qualifier("sessionFactory")
   private SessionFactory sessionFactory;

   @Autowired
   @Qualifier("HTTPConnection")
   private HTTPConnection httpConnection;

   @Autowired
   @Qualifier("ConfigDAO")
   private ConfigDAO configDAO;

   @Override
   public Map<Long, Map<Long, WordCount>> getWordSummary() {
      Map<Long, Map<Long, WordCount>> result = new HashMap<Long, Map<Long, WordCount>>();
      Session session = sessionFactory.openSession();
      String query = "SELECT w.id, web.id, wc.id, wc.count, wc.fuzzyAmount "
            + "FROM Word as w, WebReference as web, WordCount as wc "
            + "WHERE wc.word.id = w.id AND wc.webReference.id = web.id";
      Query sql = session.createQuery(query);
      @SuppressWarnings("rawtypes")
      List list = sql.list();
      for (Object array : list) {
         Object[] items = (Object[]) array;
         Long wordId = Long.parseLong(items[0].toString());
         Long webId = Long.parseLong(items[1].toString());
         long wordCountId = Long.parseLong(items[2].toString());
         int wordCount = Integer.parseInt(items[3].toString());
         String fuzzyAmount = items[4] != null ? items[4].toString() : "";
         WordCount wc = new WordCount();
         wc.setId(wordCountId);
         wc.setCount(wordCount);
         wc.setFuzzyAmount(fuzzyAmount);
         Map<Long, WordCount> mapWebReferenceWordCount = result.get(wordId);
         if (mapWebReferenceWordCount == null) {
            mapWebReferenceWordCount = new HashMap<Long, WordCount>();
         }
         mapWebReferenceWordCount.put(webId, wc);
         result.put(wordId, mapWebReferenceWordCount);
      }
      return result;
   }

   @Override
   public Map<Long, Map<Long, WordCount>> getWordSummaryOf(List<Word> wordList) {
      Map<Long, Map<Long, WordCount>> result = new HashMap<Long, Map<Long, WordCount>>();
      Session session = sessionFactory.openSession();
      String query = "SELECT w.id, web.id, wc.id, wc.count, wc.fuzzyAmount "
            + "FROM Word as w, WebReference as web, WordCount as wc "
            + "WHERE wc.word.id = w.id AND wc.webReference.id = web.id AND w.id IN (" + getWordIdList(wordList) + ")";
      Query sql = session.createQuery(query);
      @SuppressWarnings("rawtypes")
      List list = sql.list();
      for (Object array : list) {
         Object[] items = (Object[]) array;
         Long wordId = Long.parseLong(items[0].toString());
         Long webId = Long.parseLong(items[1].toString());
         long wordCountId = Long.parseLong(items[2].toString());
         int wordCount = Integer.parseInt(items[3].toString());
         String fuzzyAmount = items[4] != null ? items[4].toString() : "";
         WordCount wc = new WordCount();
         wc.setId(wordCountId);
         wc.setCount(wordCount);
         wc.setFuzzyAmount(fuzzyAmount);
         Map<Long, WordCount> mapWebReferenceWordCount = result.get(wordId);
         if (mapWebReferenceWordCount == null) {
            mapWebReferenceWordCount = new HashMap<Long, WordCount>();
         }
         mapWebReferenceWordCount.put(webId, wc);
         result.put(wordId, mapWebReferenceWordCount);
      }
      return result;
   }

   private String getWordIdList(List<Word> wordList) {
      StringBuilder result = new StringBuilder();
      for (Word word : wordList) {
         if (result.length() > 0) {
            result.append(", ");
         }
         result.append(word.getId());
      }
      return result.toString();
   }

   @Override
   public SystemConfiguration getSystemConfiguration() {
      SystemConfiguration systemConfiguration = new SystemConfiguration();

      Config proxyHost = configDAO.findByProperty(Constants.PROXY_HOST);
      Config proxyPort = configDAO.findByProperty(Constants.PROXY_PORT);
      Config wordLengthMinimum = configDAO.findByProperty(Constants.WORD_LENGTH_MINIMUM);
      Config wordLengthMaximum = configDAO.findByProperty(Constants.WORD_LENGTH_MAXIMUM);
      Config wordCountMinimum = configDAO.findByProperty(Constants.WORD_COUNT_MINIMUM);
      Config classifiedClassesCount = configDAO.findByProperty(Constants.CLASSIFIED_CLASSES_COUNT);
      Config ruleLengthMaximum = configDAO.findByProperty(Constants.RULE_LENGTH_MAXIMUM);
      Config ruleLengthMinimum = configDAO.findByProperty(Constants.RULE_LENGTH_MINIMUM);
      Config fitnessArg1 = configDAO.findByProperty(Constants.FIRST_FITNESS_ARGUMENT);
      Config fitnessArg2 = configDAO.findByProperty(Constants.SECOND_FITNESS_ARGUMENT);
      Config flooringSize = configDAO.findByProperty(Constants.FLOORING_SIZE);
      Config classifiedRuleSetSize = configDAO.findByProperty(Constants.CLASSIFIED_RULE_SET_SIZE);
      Config triangleBottomLeft = configDAO.findByProperty(Constants.TRIANGLE_BOTTOM_LEFT);
      Config triangleBottomRight = configDAO.findByProperty(Constants.TRIANGLE_BOTTOM_RIGHT);
      Config generationMaximum = configDAO.findByProperty(Constants.GENERATION_MAXIMUM);
      Config weightMinimum = configDAO.findByProperty(Constants.WEIGHT_MINIMUM);
      Config includeManualRules = configDAO.findByProperty(Constants.INCLUDE_MANUAL_RULES);

      systemConfiguration.setProxyHost(proxyHost != null ? proxyHost.getValue() : "");
      systemConfiguration.setProxyPort(proxyPort != null ? Integer.parseInt(proxyPort.getValue()) : 0);
      systemConfiguration.setNumberOfClasses(classifiedClassesCount != null ? Integer.parseInt(classifiedClassesCount
            .getValue()) : 0);

      for (int i = 0; i < systemConfiguration.getNumberOfClasses(); i++) {
         Config className = configDAO.findByProperty(String.valueOf(i + 1));
         if (className != null) {
            systemConfiguration.getClasses().put(String.valueOf(i + 1), className.getValue());
         } else {
            systemConfiguration.getClasses().put(String.valueOf(i + 1), String.valueOf(i + 1));
         }
      }

      if (wordCountMinimum != null) {
         systemConfiguration.setWordCountMinimum(Integer.parseInt(wordCountMinimum.getValue()));
      }
      if (wordLengthMaximum != null) {
         systemConfiguration.setWordLengthMaximum(Integer.parseInt(wordLengthMaximum.getValue()));
      }
      if (wordLengthMinimum != null) {
         systemConfiguration.setWordLengthMinimum(Integer.parseInt(wordLengthMinimum.getValue()));
      }
      if (ruleLengthMaximum != null) {
         systemConfiguration.setRuleLengthMaximum(Integer.parseInt(ruleLengthMaximum.getValue()));
      }
      if (ruleLengthMinimum != null) {
         systemConfiguration.setRuleLengthMinimum(Integer.parseInt(ruleLengthMinimum.getValue()));
      }
      if (fitnessArg1 != null) {
         systemConfiguration.setFitnessArgument1(Double.parseDouble(fitnessArg1.getValue()));
      }
      if (fitnessArg2 != null) {
         systemConfiguration.setFitnessArgument2(Double.parseDouble(fitnessArg2.getValue()));
      }
      if (flooringSize != null) {
         systemConfiguration.setFlooringSize(Integer.parseInt(flooringSize.getValue()));
      }
      if (classifiedRuleSetSize != null) {
         systemConfiguration.setClassifiedRuleSetSize(Integer.parseInt(classifiedRuleSetSize.getValue()));
      }

      if (triangleBottomLeft != null) {
         systemConfiguration.setTriangleBottomLeft(Double.parseDouble(triangleBottomLeft.getValue()));
      }

      if (triangleBottomRight != null) {
         systemConfiguration.setTriangleBottomRight(Double.parseDouble(triangleBottomRight.getValue()));
      }

      if (generationMaximum != null) {
         systemConfiguration.setGenerationMaximum(Integer.parseInt(generationMaximum.getValue()));
      }

      if (weightMinimum != null) {
         systemConfiguration.setWeightMinimum(Double.parseDouble(weightMinimum.getValue()));
      }

      if (includeManualRules != null) {
         systemConfiguration.setIncludeManualRules(Boolean.parseBoolean(includeManualRules.getValue()));
      }
      
      return systemConfiguration;
   }

   @Override
   public String saveSystemConfiguration(SystemConfiguration systemConfiguration) {
      String status = null;
      Config proxyHost = getConfigObject(Constants.PROXY_HOST);
      proxyHost.setValue(systemConfiguration.getProxyHost());

      Config proxyPort = getConfigObject(Constants.PROXY_PORT);
      proxyPort.setValue(String.valueOf(systemConfiguration.getProxyPort()));

      Config wordLengthMinimum = getConfigObject(Constants.WORD_LENGTH_MINIMUM);
      wordLengthMinimum.setValue(String.valueOf(systemConfiguration.getWordLengthMinimum()));

      Config wordLengthMaximum = getConfigObject(Constants.WORD_LENGTH_MAXIMUM);
      wordLengthMaximum.setValue(String.valueOf(systemConfiguration.getWordLengthMaximum()));

      Config wordCountMinimum = getConfigObject(Constants.WORD_COUNT_MINIMUM);
      wordCountMinimum.setValue(String.valueOf(systemConfiguration.getWordCountMinimum()));

      Config classifiedClassCount = getConfigObject(Constants.CLASSIFIED_CLASSES_COUNT);
      classifiedClassCount.setValue(String.valueOf(systemConfiguration.getNumberOfClasses()));

      Config ruleLengthMaximum = getConfigObject(Constants.RULE_LENGTH_MAXIMUM);
      ruleLengthMaximum.setValue(String.valueOf(systemConfiguration.getRuleLengthMaximum()));

      Config ruleLengthMinimum = getConfigObject(Constants.RULE_LENGTH_MINIMUM);
      ruleLengthMinimum.setValue(String.valueOf(systemConfiguration.getRuleLengthMinimum()));
      
      Config fitnessArg1 = getConfigObject(Constants.FIRST_FITNESS_ARGUMENT);
      fitnessArg1.setValue(String.valueOf(systemConfiguration.getFitnessArgument1()));
      
      Config fitnessArg2 = getConfigObject(Constants.SECOND_FITNESS_ARGUMENT);
      fitnessArg2.setValue(String.valueOf(systemConfiguration.getFitnessArgument2()));

      Config flooringSize = getConfigObject(Constants.FLOORING_SIZE);
      flooringSize.setValue(String.valueOf(systemConfiguration.getFlooringSize()));

      Config classifiedRuleSetSize = getConfigObject(Constants.CLASSIFIED_RULE_SET_SIZE);
      classifiedRuleSetSize.setValue(String.valueOf(systemConfiguration.getClassifiedRuleSetSize()));

      Config generationMaximum = getConfigObject(Constants.GENERATION_MAXIMUM);
      generationMaximum.setValue(String.valueOf(systemConfiguration.getGenerationMaximum()));

      Config triangleBottomLeft = getConfigObject(Constants.TRIANGLE_BOTTOM_LEFT);
      triangleBottomLeft.setValue(String.valueOf(systemConfiguration.getTriangleBottomLeft()));

      Config triangleBottomRight = getConfigObject(Constants.TRIANGLE_BOTTOM_RIGHT);
      triangleBottomRight.setValue(String.valueOf(systemConfiguration.getTriangleBottomRight()));

      Config weightMinimum = getConfigObject(Constants.WEIGHT_MINIMUM);
      weightMinimum.setValue(String.valueOf(systemConfiguration.getWeightMinimum()));

      Config includeManualRules = getConfigObject(Constants.INCLUDE_MANUAL_RULES);
      includeManualRules.setValue(String.valueOf(systemConfiguration.isIncludeManualRules()));

      Session session = sessionFactory.openSession();
      Transaction tx = null;

      try {
         tx = session.beginTransaction();
         session.saveOrUpdate(proxyHost);
         session.saveOrUpdate(proxyPort);
         session.saveOrUpdate(wordCountMinimum);
         session.saveOrUpdate(wordLengthMinimum);
         session.saveOrUpdate(wordLengthMaximum);
         session.saveOrUpdate(classifiedClassCount);
         session.saveOrUpdate(ruleLengthMaximum);
         session.saveOrUpdate(ruleLengthMinimum);
         session.saveOrUpdate(fitnessArg1);
         session.saveOrUpdate(fitnessArg2);
         session.saveOrUpdate(flooringSize);
         session.saveOrUpdate(classifiedRuleSetSize);
         session.saveOrUpdate(triangleBottomLeft);
         session.saveOrUpdate(triangleBottomRight);
         session.saveOrUpdate(generationMaximum);
         session.saveOrUpdate(weightMinimum);
         session.saveOrUpdate(includeManualRules);
         if (systemConfiguration.getNumberOfClasses() > 0) {
            for (int i = 0; i < systemConfiguration.getNumberOfClasses(); i++) {
               Config className = getConfigObject(String.valueOf(i + 1));
               className.setValue(systemConfiguration.getClasses().get(String.valueOf(i + 1)));
               session.saveOrUpdate(className);
            }
         }
         httpConnection.setProxy(proxyHost.getValue(), Integer.parseInt(proxyPort.getValue()));
         status = Constants.UPDATE_SYSTEM_CONFIGURATION_OK;
         tx.commit();
      } catch (Exception e) {
         if (tx != null) {
            tx.rollback();
         }
         status = "Could not update configuration.";
      } finally {
         session.close();
      }

      if (Constants.UPDATE_SYSTEM_CONFIGURATION_OK.equals(status)) {
         executeCallback();
      }

      return status;
   }

   @Override
   public Map<Long, List<ConditionData>> findAllTrainingData() {
      Session session = sessionFactory.openSession();
      Map<Long, List<ConditionData>> result = new HashMap<Long, List<ConditionData>>();

      Query findData = session.createQuery("SELECT web.id, w.label, wc.fuzzyAmount "
            + "FROM WordCount as wc, Word as w, WebReference as web "
            + "WHERE wc.fuzzyAmount is not null AND wc.word.id = w.id AND wc.webReference.id = web.id");
      @SuppressWarnings("rawtypes")
      List objects = findData.list();
      for (Object array : objects) {
         Object[] values = (Object[]) array;
         Long webReferenceId = Long.parseLong(String.valueOf(values[0]));
         String wordLabel = String.valueOf(values[1]);
         String fuzzyAmount = String.valueOf(values[2]);
         List<ConditionData> conditionDataList = result.get(webReferenceId);
         if (conditionDataList == null) {
            conditionDataList = new ArrayList<ConditionData>();
         }
         conditionDataList.add(new ConditionData(wordLabel, fuzzyAmount));
         result.put(webReferenceId, conditionDataList);
      }

      session.close();
      return result;
   }

   private Config getConfigObject(String propertyName) {
      Config configObject = configDAO.findByProperty(propertyName);
      if (configObject == null) {
         configObject = new Config();
         configObject.setProperty(propertyName);
      }
      return configObject;
   }
}
